﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Primitives;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using NLog.Fluent;

namespace Labthink.PCSoft.UniversalHost4.Utility
{
    public static class ExtensionMethod
    {
        /// <summary>
        /// 列表深度复制
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="listToClone"></param>
        /// <returns></returns>
        public static List<T> Clone<T>(this List<T> listToClone) where T : ICloneable
        {
            if (listToClone == null)
            {
                return null;
            }

            var tmp = listToClone.Select(item => (T)item.Clone());

            return tmp.ToList();
        }

        public static Dictionary<TKey, TValue> Clone<TKey, TValue>
            (this Dictionary<TKey, TValue> original) where TValue : ICloneable
        {
            var ret = new Dictionary<TKey, TValue>(original.Count,
                original.Comparer);
            foreach (KeyValuePair<TKey, TValue> entry in original)
            {
                ret.Add(entry.Key, (TValue)entry.Value.Clone());
            }

            return ret;
        }

        public static int ToInt(this object o)
        {
            if (o is DBNull)
            {
                return -1;
            }

            return Convert.ToInt32(o);
        }

        public static bool ToBool(this object o)
        {
            if (o is DBNull)
            {
                return false;
            }

            return string.Equals(o?.ToString(), "true", StringComparison.InvariantCultureIgnoreCase);
        }

        public static int ToInt(this string s)
        {
            return Convert.ToInt32(s);
        }

        public static byte ToByte(this string s)
        {
            return Convert.ToByte(s);
        }

        public static List<byte> ToBytes(this string s, int count)
        {
            byte[] bytes = BitConverter.GetBytes(s.ToInt());
            var reversed = bytes.Reverse();
            var r = reversed.Skip(4 - count).Take(count).ToList();

            return r;
        }

        public static List<byte> ToBytes(this int i, int count)
        {
            byte[] bytes = BitConverter.GetBytes(i);
            var reversed = bytes.Reverse();
            var r = reversed.Skip(4 - count).Take(count).ToList();

            return r;
        }

        /// <summary>
        /// 返回字符串的最后n个字符
        /// </summary>
        /// <param name="str"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static string Last(this string str, int n)
        {
            if (n > str.Length)
            {
                n = str.Length;
            }

            return str.Substring(str.Length - n, n);
        }

        /// <summary>
        /// 返回字符串的前n个字符
        /// </summary>
        /// <param name="str"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static string First(this string str, int n)
        {
            if (n > str.Length)
            {
                n = str.Length;
            }

            return str.Substring(0, n);
        }

        public static int ToInt32(this string s)
        {
            return ToInt(s);
        }

        public static int ToInt32(this object o)
        {
            if (o == null) return 0;
            if (string.IsNullOrEmpty(o.ToString()))
                return 0;

            return Convert.ToInt32(o);
        }

        public static int ToInt(this char c)
        {
            return Convert.ToInt32(c.ToString());
        }

        public static double ToDouble(this object o)
        {
            if (o is DBNull)
            {
                return -1;
            }

            return Convert.ToDouble(o);
        }

        public static double ToDouble(this string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return 0;
            }

            return Convert.ToDouble(s);
        }

        public static decimal ToDecimal(this string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return 0;
            }

            return Convert.ToDecimal(s);
        }


        public static DateTime ToDateTime(this string s)
        {
            if (string.IsNullOrEmpty(s)) return DateTime.MinValue;
            return Convert.ToDateTime(s);
        }

        public static DateTime ToDateTime(this object o)
        {
            if (string.IsNullOrEmpty(o.ToString())) return DateTime.MinValue;
            return Convert.ToDateTime(o);
        }

        public static string Round(this double d, int digits)
        {
            string format = "0";
            if (digits > 0)
            {
                for (int j = 0; j < digits; j++)
                {
                    format += "0";
                }

                format = format.Insert(1, ".");
            }

            return d.ToString(format);
        }

        public static double Round(this object d, int digits)
        {
            string format = "0";
            if (digits > 0)
            {
                for (int j = 0; j < digits; j++)
                {
                    format += "0";
                }
                format = format.Insert(1, ".");
            }

            return double.Parse(d.ToDouble().ToString(format));
        }

        public static bool IsNumeric(this string str)
        {
            try
            {
                str.ToDouble();
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool IsInt(this string str)
        {
            return int.TryParse(str, out int _);
        }

        public static string ScientificNotation(this double d, int pre)
        {
            string f = "e" + pre;
            char[] source;
            string s;
            string[] tmp;
            int index = -1;
            string r;
            try
            {
                s = d.ToString(f, CultureInfo.InvariantCulture);

                tmp = s.Split('e');
                source = tmp[1].ToCharArray();
                for (int i = 1; i < source.Length; i++)
                {
                    if (source[i] != '0')
                    {
                        index = i;
                        break;
                    }
                }

                if (index != -1)
                {
                    r = tmp[1].Substring(index);
                }
                else
                {
                    r = "0";
                }
            }
            catch
            {
                return d + "";
            }


            return string.Concat(tmp[0], "e", source[0], r);
        }

        // ReSharper disable once InconsistentNaming
        public static double ScientificNotation(this object d, int pre)
        {
            string f = "e" + pre;
            string s = Convert.ToDouble(d).ToString(f, CultureInfo.InvariantCulture);

            string[] tmp = s.Split('e');
            char[] source = tmp[1].ToCharArray();
            int index = -1;
            for (int i = 1; i < source.Length; i++)
            {
                if (source[i] != '0')
                {
                    index = i;
                    break;
                }
            }

            string r;
            if (index != -1)
            {
                r = tmp[1].Substring(index);
            }
            else
            {
                r = "0";
            }

            return Convert.ToDouble(string.Concat(tmp[0], "e", source[0], r));
        }

        public static string ScientificNotationStr(this object d, int pre)
        {
            string f = "e" + pre;
            string s = Convert.ToDouble(d).ToString(f, CultureInfo.InvariantCulture);

            string[] tmp = s.Split('e');
            char[] source = tmp[1].ToCharArray();
            int index = -1;
            for (int i = 1; i < source.Length; i++)
            {
                if (source[i] != '0')
                {
                    index = i;
                    break;
                }
            }

            string r;
            if (index != -1)
            {
                r = tmp[1].Substring(index);
            }
            else
            {
                r = "0";
            }

            return string.Concat(tmp[0], "e", source[0], r);
        }

        public static T ToEnum<T>(this string value)
        {
            return (T)Enum.Parse(typeof(T), value, true);
        }

        /// <summary>
        /// https://msdn.microsoft.com/en-us/library/system.windows.forms.form.centertoscreen.aspx
        /// 不适用自带的Form.CenterToScreen Method ()是因为
        ///Do not call this directly from your code.Instead, set the StartPosition property to CenterScreen.
        ///The CenterToScreen method uses the following priority list to determine the screen used to center the form:
        ///The Owner property of the form.
        ///The HWND owner of the form.
        ///The screen that currently has the mouse cursor.
        /// </summary>
        /// <param name="f"></param>
        public static void RealCenterToScreen(this Form f)
        {
            Screen screen = Screen.FromControl(f);

            Rectangle workingArea = screen.WorkingArea;
            f.Location = new System.Drawing.Point
            {
                X = Math.Max(workingArea.X, workingArea.X + (workingArea.Width - f.Width) / 2),
                Y = Math.Max(workingArea.Y, workingArea.Y + (workingArea.Height - f.Height) / 2)
            };
        }

        public static string GetRandomString(int length)
        {
            var chars = new List<char>();
            // A-Z
            for (char c = 'A'; c <= 'Z'; c++)
            {
                chars.Add(c);
            }

            // a-z
            for (char c = 'a'; c <= 'z'; c++)
            {
                chars.Add(c);
            }

            // 0-9
            for (char c = '0'; c <= '9'; c++)
            {
                chars.Add(c);
            }

            chars.Add('-');
            chars.Add('_');

            var stringChars = new char[length];

            for (int i = 0; i < stringChars.Length; i++)
            {
                stringChars[i] = chars[StaticRandom.Next(chars.Count)];
            }

            return new string(stringChars);
        }

        public static System.Windows.Media.Color ToColor(this string strColor)
        {
            return (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(strColor);
        }

        /// <summary>
        /// Attempts to retrieve the real cause of a composition failure.
        /// </summary>
        /// <remarks>
        /// Sometimes a MEF composition fails because an exception occurs in the ctor of a type we're trying to
        /// create. Unfortunately, the CompositionException doesn't make that easily available, so we don't get
        /// that info in haystack. This method tries to find that exception as that's really the only one we care
        /// about if it exists. If it can't find it, it returns the original composition exception.
        /// </remarks>
        /// <param name="exception"></param>
        /// <returns></returns>
        public static Exception UnwrapCompositionException(this Exception exception)
        {
            if (!(exception is CompositionException compositionException))
            {
                return exception;
            }

            var unwrapped = compositionException;
            while (unwrapped != null)
            {
                var firstError = unwrapped.Errors.FirstOrDefault();

                var currentException = firstError?.Exception;

                if (currentException == null)
                {
                    break;
                }

                if (currentException is ComposablePartException composablePartException
                    && composablePartException.InnerException != null)
                {
                    if (!(composablePartException.InnerException is CompositionException innerCompositionException))
                    {
                        return currentException.InnerException ?? exception;
                    }
                    currentException = innerCompositionException;
                }

                unwrapped = currentException as CompositionException;
            }

            return exception; // Couldn't find the real deal. Throw the original.
        }

        public static string ToString(this byte[] bytes)
        {
            var str = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            return str;
        }

        public static IEnumerable<T> DequeueChunk<T>(this ConcurrentQueue<T> queue, int chunkSize)
        {
            List<T> requestChunk = new List<T>();
            for (var i = 0; i < chunkSize; i++)
            {
                if (queue.TryDequeue(out T req))
                    requestChunk.Add(req);
            }

            return requestChunk;
        }

        public static string GetLanguageValue(this ResourceDictionary rd, string key)
        {
            if (rd == null)
            {
                return "";
            }

            object obj = rd[key];
            return obj?.ToString() ?? key;
        }

        public static void AddRange<T>(this ObservableCollection<T> ov, IEnumerable<T> list)
        {
            foreach (T item in list)
            {
                ov.Add(item);
            }
        }

        public static IDisposable CreateTimeoutScope(this IDisposable disposable, TimeSpan timeSpan)
        {
            var cancellationTokenSource = new CancellationTokenSource(timeSpan);
            var cancellationTokenRegistration = cancellationTokenSource.Token.Register(disposable.Dispose);
            return new DisposableScope(
                () =>
                {
                    cancellationTokenRegistration.Dispose();
                    cancellationTokenSource.Dispose();
                    disposable.Dispose();
                });
        }

        public static string Clear(this string myString)
        {
            return myString.Replace("\uFEFF", "");
        }

        /// <summary>
        /// 将字符串转成二进制
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string ToHex(this string s)
        {
            return BitConverter.ToString(
                Encoding.Default.GetBytes(s)).Replace("-", " ");
        }

        public static string ToStr(this string mHex) // 返回十六进制代表的字符串
        {
            mHex = mHex.Replace(" ", "");
            if (mHex.Length <= 0) return "";
            byte[] vBytes = new byte[mHex.Length / 2];
            for (int i = 0; i < mHex.Length; i += 2)
                if (!byte.TryParse(mHex.Substring(i, 2), NumberStyles.HexNumber, null, out vBytes[i / 2]))
                    vBytes[i / 2] = 0;
            return Encoding.Default.GetString(vBytes);
        }

        public static bool? ShowDialog(this Window win, Window owner)
        {
            bool? r = false;
            try
            {
                win.Owner = owner;
                win.ShowInTaskbar = false;
                r = win.ShowDialog();
            }
            catch (Exception e)
            {
                Log.Error().Exception(e);
            }

            return r;
        }

        public static void Show(this Window win, Window owner)
        {
            try
            {
                win.Topmost = true;
                win.Owner = owner;
                win.ShowInTaskbar = false;
                win.Show();
            }
            catch (Exception e)
            {
                Log.Error().Exception(e);
            }
        }

        /// <summary>
        /// 半角-全角
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ToSbc(this string input)
        {
            // 半角转全角：    
            char[] array = input.ToCharArray();
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] == 32)
                {
                    array[i] = (char)12288;
                    continue;
                }
                if (array[i] < 127)
                {
                    array[i] = (char)(array[i] + 65248);
                }
            }
            return new string(array);
        }

        /// <summary>
        /// 全角-半角
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ToDbc(this string input)
        {
            char[] array = input.ToCharArray();
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] == 12288)
                {
                    array[i] = (char)32;
                    continue;
                }
                if (array[i] > 65280 && array[i] < 65375)
                {
                    array[i] = (char)(array[i] - 65248);
                }
            }
            return new string(array);
        }

        public static bool IsMatch(this string input, string pattern)
        {
            return Regex.IsMatch(input, pattern);
        }

        public static string Match(this string input, string pattern)
        {
            return Regex.Match(input, pattern).Value;
        }

        public static void Clear<T>(this ConcurrentQueue<T> queue)
        {
            while (queue.TryDequeue(out T _))
            {
                // do nothing
            }
        }

        public static void Enqueue<T>(this ConcurrentQueue<T> queue, T[] datas)
        {
            foreach (T data in datas)
            {
                queue.Enqueue(data);
            }
        }
    }
}
